/**
 * @file   Matrix.h
 * @author Linfufu
 * @date   Thu Oct  7 22:54:27 2021
 * 
 * @brief  
 * 
 * 
 */

#ifndef _LINFUFU_MATRIX
#define _LINFUFU_MATRIX

#include <iostream>
#include <new>
#include <vector>
#include <exception>
#include <cmath>
#include <limits>

template <typename T>
class Matrix;//声明基类

template < typename T >
class RowVector;//声明派生类

template <typename T >
class ColVector;//声明派生类

template <typename T>
Matrix<T> operator*(const Matrix<T>& a,const  Matrix<T>& b);

template <typename T>
Matrix<T> operator*(const ColVector<T>& a, const RowVector<T>& b);

template <typename T>
T operator*(const RowVector<T>& a,const  ColVector<T>& b);

template <typename T>
T operator*(const RowVector<T>& a, const RowVector<T>& b);

template <typename T>
T operator*(const ColVector<T>& a,const ColVector<T>& b);

template <typename T>
class Matrix
{
    protected:
        int n_row;
        int n_col;
        T* matrix_elements;

    public:
        Matrix()//声明新元素
        {
            matrix_elements = nullptr;
        }
        Matrix(int r, int c)//声名行数和列数
        {
            n_row = r;
            n_col = c;
            AllocMem(r*c);
        }
        ~Matrix()//析构函数，摧毁数据
        {
            ReleaseMem();
        }
        void Error(int level)//若输入不符合要求
        {
            switch (level)
            {
                case 0:
                    std::cout << "Allocating memory fail!" << std::endl;
                    break;

                case 1:
                    std::cout <<  " Unmatched matrice ! " << std::endl;
                    break;

                case 2:
                    std::cout <<  " numbers of vector elements do not match ! " << std::endl;
                    break;

                default:
                    break;
            }

        }
        void AllocMem(int n)
        {   
            try{matrix_elements = new T [n];}
            catch (std::bad_alloc& no_memory)
            {   
	            std::cerr << no_memory.what() << std::endl;
	            Error(0);
            }
            matrix_elements = new T[n];
        }
        void ReleaseMem()
        {
            if (matrix_elements != nullptr)
	        {
                delete[] matrix_elements;
                matrix_elements = nullptr;
            }
        }
        void Inputdata(const double* input, int m, int n)//用m表示行数，n表示列数
        {
            for (int i = 0; i < m ; i++)
                for(int j = 0; j < n ; j++)
                    matrix_elements[ i * n + j ] = input[i*n + j];
        };

        void log()//打印矩阵
        {
            for(int i=0 ; i < n_row; i++ )
            {
                for(int j = 0; j < n_col; j++)
                {
                    std::cout<< *( matrix_elements + i * n_col + j )<<" ";
                }
                std::cout<<std::endl;
            }
        }

        bool is_square()//判断矩阵是否为方阵
        {
            bool ans = false;
            if (n_row  == n_col)
                ans = true;
            return ans;
        }

        friend 
        Matrix<T> operator * <> (const Matrix<T>& a, const Matrix<T>& b);
        friend
        Matrix<T> operator* <> (const ColVector<T>& a,const  RowVector<T>& b);
        friend
        T operator* <> (const RowVector<T>& a,const  ColVector<T>& b);
        friend
        T operator* <> (const RowVector<T>& a,const  RowVector<T>& b);
        friend
        T operator* <> (const ColVector<T>& a, const ColVector<T>& b);
};

template < typename T >//派生行向量
class RowVector : public Matrix<T>
{
    public:
        RowVector()
        {
            this->matrix_elements = nullptr;
        }

        RowVector(int n)
        {
            this->n_col = n;
            this->n_row = 1;
            this->AllocMem(n);
        }

        ~RowVector()
        {
            this->ReleaseMem();
        }
        void Inputdata(const double* input, int m)
        {
            for (int i = 0; i < m ; i++)
                this->matrix_elements[ i ] = input[ i ];
        };
        
        friend 
        Matrix<T> operator * <> (const Matrix<T>& a,const  Matrix<T>& b);
        friend
        Matrix<T> operator* <> (const ColVector<T>& a, const RowVector<T>& b);
        friend
        T operator* <> (const RowVector<T>& a, const ColVector<T>& b);
        friend
        T operator* <> (const RowVector<T>& a, const RowVector<T>& b);
        friend
        T operator* <> (const ColVector<T>& a, const ColVector<T>& b);
};

template <typename T >//派生列向量
class ColVector: public Matrix<T>
{   
    public:
        ColVector()
        {
            this->matrix_elements = nullptr;
        }
        ColVector(int n)
        {
            this->n_col = 1;
            this->n_row = n;
            this->AllocMem(n);
        }
        ~ColVector()
        {
            this->ReleaseMem();
        }
        void Inputdata(const double* input, int m)
        {
            for (int i = 0; i < m ; i++)
                this->matrix_elements[ i ] = input[ i ];
        };
        friend 
        Matrix<T> operator * <> (const Matrix<T>& a, const Matrix<T>& b);
        friend
        Matrix<T> operator* <> (const ColVector<T>& a, const RowVector<T>& b);
        friend
        T operator* <> (const RowVector<T>& a, const ColVector<T>& b);
        friend
        T operator* <> (const RowVector<T>& a,const  RowVector<T>& b);
        friend
        T operator* <> (const ColVector<T>& a, const ColVector<T>& b);

};


template <typename T>//两个矩阵的乘积
Matrix<T> operator*(const Matrix<T>& a, const Matrix<T>& b)
{
    Matrix<T> ans(a.n_row,b.n_col);
    if(a.n_col != b.n_row)
        {
            ans.Error(1);
            return ans;
        }

    int iter = b.n_row;
        
    for(int i = 0 ; i < a.n_row; i++)
        for(int j = 0 ; j < b.n_col; j++)
            {
                ans.matrix_elements[ i*b.n_col + j ] = 0;
                for (int k = 0;k < iter ;k++)
                    ans.matrix_elements[ i*b.n_col + j ] += a.matrix_elements[ i * a.n_col + k ]  * b.matrix_elements[k * b.n_col + j];
            }
    
    return ans;
};


template <typename T>//列向量和行向量的乘积
Matrix<T> operator*(const ColVector<T>& a, const RowVector<T>& b)
{
    Matrix<T> ans(a.n_row,b.n_col);
    if(a.n_col != b.n_row)
        {
            ans.Error(2);
            return ans;
        }
    for(int i = 0; i < a.n_row ; i++)
        for(int j = 0; j < b.n_col;j++)
            {
                ans.matrix_elements[i*b.n_col + j] = a.matrix_elements[i]*b.matrix_elements[j];
            }
    return ans;
}

template <typename T>//返回俩矩阵乘积
T operator*(const RowVector<T>& a, const ColVector<T>& b)
{
    T ans = 0;
    Matrix<T> err;
    if(a.n_col != b.n_row)
    {
        err.Error(2);
        return ans;
    }
    for(int i = 0; i < a.n_col ; i++)
        ans += a.matrix_elements[i]*b.matrix_elements[i];
    return ans;
}


template <typename T>//返回两列向量内积
T operator*(const ColVector<T>& a, const ColVector<T>& b)
{
    T ans = 0;
    Matrix<T> err;
    if(a.n_row != b.n_row)
    {
        err.Error(2);
        return ans;
    }   
    for(int i = 0; i < a.n_row ; i++)
        ans += a.matrix_elements[i]*b.matrix_elements[i];
    return ans;
}

template <typename T>//返回俩行向量内积
T operator*(const RowVector<T>& a, const RowVector<T>& b)
{
    T ans = 0;
    Matrix<T> err;
    if(a.n_col != b.n_col)
    {
        err.Error(2);
        return ans;
    }
    for(int i = 0; i < a.n_col ; i++)
        ans += a.matrix_elements[i]*b.matrix_elements[i];
    return ans;
}

#else
#endif
